package com.dobbinsoft.iotechn.device.service.open;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dobbinsoft.iotechn.core.constant.Const;
import com.dobbinsoft.iotechn.core.exception.ServiceException;
import com.dobbinsoft.iotechn.core.model.Page;
import com.dobbinsoft.iotechn.data.compent.CacheComponent;
import com.dobbinsoft.iotechn.device.api.open.ProductDevService;
import com.dobbinsoft.iotechn.device.constant.DeviceConst;
import com.dobbinsoft.iotechn.device.domain.*;
import com.dobbinsoft.iotechn.device.dto.ProductDevDTO;
import com.dobbinsoft.iotechn.device.exception.DeviceExceptionDefinition;
import com.dobbinsoft.iotechn.device.exception.DeviceServiceException;
import com.dobbinsoft.iotechn.device.mapper.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Description:
 * User: rize
 * Date: 2020/6/17
 * Time: 15:00
 */
@Service("productDevService")
public class ProductDevServiceImpl implements ProductDevService {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceDataMapper deviceDataMapper;

    @Autowired
    private DeviceUserMapper deviceUserMapper;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductDataMapper productDataMapper;

    @Autowired
    private ProductControllerMapper productControllerMapper;

    @Autowired
    private ControllerComponentMapper controllerComponentMapper;

    @Autowired
    private ControllerComponentAttrMapper controllerComponentAttrMapper;

    @Autowired
    private CacheComponent cacheComponent;

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String create(ProductDevDTO product, Long developerId) throws ServiceException {
        // 1. 参数校验
        if (StringUtils.isEmpty(product.getTitle()) || StringUtils.isEmpty(product.getImg())) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
        }
        Date now = new Date();
        this.checkProductPram(product, now,true);
        // 2. 添加设备
        ProductDO productDO = new ProductDO();
        productDO.setTitle(product.getTitle());
        productDO.setImg(product.getImg());
        productDO.setDescription(product.getDescription());
        productDO.setDeveloperId(developerId);
        productDO.setGmtCreate(now);
        productDO.setGmtUpdate(now);
        if (productMapper.insert(productDO) > 0) {
            if (!CollectionUtils.isEmpty(product.getDataList())) {
                productDataMapper.batchInsert(productDO.getId(), product.getDataList());
            }
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_UNKNOWN_EXCEPTION);
    }

    @Override
    @DS(Const.DB_S)
    public Page<ProductDevDTO> list(Integer pageNo, Integer pageSize, String title, Long developerId) throws ServiceException {
        return productMapper.getProductPage(Page.div(pageNo, pageSize, ProductDevDTO.class), title, developerId);
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String delete(Long productId, Long developerId) throws ServiceException {
        Integer count = deviceMapper.selectCount(new QueryWrapper<DeviceDO>().eq("product_id", productId));
        if (count > 0) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PRODUCT_HAS_BUILT_DEVICE);
        }
        if (productMapper.delete(
                new QueryWrapper<ProductDO>()
                        .eq("id", productId)
                        .eq("developer_id", developerId)) > 0) {
            this.deleteProductBase(productId);
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_UNKNOWN_EXCEPTION);
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String deleteForce(Long productId, Long developerId) throws ServiceException {
        if (productMapper.delete(
                new QueryWrapper<ProductDO>()
                        .eq("id", productId)
                        .eq("developer_id", developerId)) > 0) {
            this.deleteProductBase(productId);
            // 删除设备
            List<DeviceDO> willDeleteDeviceDOList = deviceMapper.selectList(
                    new QueryWrapper<DeviceDO>()
                            .select("id")
                            .select("access_key_id")
                            .eq("product_id", productId));
            List<Long> deviceIds = willDeleteDeviceDOList.stream().map(DeviceDO::getId).collect(Collectors.toList());
            List<String> deviceAccessKeyIds = willDeleteDeviceDOList.stream().map(DeviceDO::getAccessKeyId).collect(Collectors.toList());
            deviceMapper.delete(new QueryWrapper<DeviceDO>().eq("product_id", productId));
            // 删除设备数据点
            deviceDataMapper.delete(new QueryWrapper<DeviceDataDO>().in("device_id", deviceIds));
            List<String> deviceDataHashKeyList = willDeleteDeviceDOList.stream().map(item -> DeviceConst.CA_DEVICE_DATA_HASH + item.getId()).collect(Collectors.toList());
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 删除缓存，但是要等事务提交之后才删除
                    cacheComponent.delHashKeyList(DeviceConst.CA_DEVICE_HASH, deviceAccessKeyIds);
                    cacheComponent.delList(deviceDataHashKeyList);
                }
            });
            // 删除设备用户
            deviceUserMapper.delete(new QueryWrapper<DeviceUserDO>().in("device_id", deviceIds));
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_UNKNOWN_EXCEPTION);
    }

    private void deleteProductBase(Long productId) {
        // 1. 删除数据点
        productDataMapper.delete(new QueryWrapper<ProductDataDO>().eq("product_id", productId));
        // 2. 删除controller
        List<Long> controllerIds = productControllerMapper.selectList(
                new QueryWrapper<ProductControllerDO>()
                        .select("id")
                        .eq("product_id", productId)).stream().map(ProductControllerDO::getId).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(controllerIds)) {
            productControllerMapper.delete(new QueryWrapper<ProductControllerDO>().eq("product_id", productId));
            // 2.1. 删除遥控器组件
            controllerComponentMapper.delete(new QueryWrapper<ControllerComponentDO>().in("controller_id", controllerIds));
            // 2.2. 删除遥控器组件属性
            controllerComponentAttrMapper.delete(new QueryWrapper<ControllerComponentAttrDO>().in("controller_id", controllerIds));
        }
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String edit(ProductDevDTO product, Long developerId) throws ServiceException {
        // 1. 参数校验
        if (product.getId() == null) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
        }
        Date now = new Date();
        this.checkProductPram(product, now, false);
        // 2. 编辑
        // 2.1. 编辑主表
        ProductDO productDO = new ProductDO();
        productDO.setId(product.getId());
        productDO.setTitle(product.getTitle());
        productDO.setImg(product.getImg());
        productDO.setDeveloperId(developerId);
        productDO.setDescription(StringUtils.isEmpty(product.getDescription()) ? "" : product.getDescription());
        productDO.setGmtCreate(now);
        productDO.setGmtUpdate(now);
        if (productMapper.update(productDO,
                new QueryWrapper<ProductDO>()
                        .eq("id", product.getId())
                        .eq("developer_id", developerId)) > 0) {
            // 2.2. 编辑数据点表
            // 当前需要的数据点
            List<ProductDataDO> dataList = product.getDataList();
            // 当前存在的数据点
            List<Long> dataListFromDB = productDataMapper.getIdsByProductIds(product.getId());
            // 将多余的数据点删除
            // 新增加的数据没有Id，将有Id的全部找出来，取个差集就是要删除的
            List<ProductDataDO> editList = dataList.stream().filter(item -> item.getId() != null).collect(Collectors.toList());
            List<Long> reservedIds = editList.stream().map(item -> item.getId()).collect(Collectors.toList());
            // 排除还保留的Id (剩余的就是不需要的，即要删除的)
            dataListFromDB.removeAll(reservedIds);
            if (!CollectionUtils.isEmpty(dataListFromDB)) {
                productDataMapper.deleteBatchIds(dataListFromDB);
                // 将设备多余的数据点删除
                List<DeviceDataDO> willDeleteDataList = deviceDataMapper.selectList(new QueryWrapper<DeviceDataDO>().select("device_id", "id_tag").in("product_data_id", dataListFromDB));
                deviceDataMapper.delete(new QueryWrapper<DeviceDataDO>().in("product_data_id", dataListFromDB));
                // 待提交事务后，删除缓存
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {
                        for (DeviceDataDO deviceDataDO : willDeleteDataList) {
                            cacheComponent.delHashKey(DeviceConst.CA_DEVICE_DATA_HASH + deviceDataDO.getId(), deviceDataDO.getIdTag());
                        }
                    }
                });

            }
            // 更新存在Id的
            for (ProductDataDO editDO : editList) {
                productDataMapper.updateById(editDO);
            }
            // 添加不存在Id
            List<ProductDataDO> createList = dataList.stream().filter(item -> item.getId() == null).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(createList)) {
                productDataMapper.batchInsert(productDO.getId(), createList);
            }
            return "ok";
        }
        throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_UNKNOWN_EXCEPTION);
    }

    @Override
    @DS(Const.DB_S)
    public ProductDevDTO detail(Long productId, Long developerId) throws ServiceException {
        ProductDevDTO devDTO = new ProductDevDTO();
        ProductDO productDO = productMapper.selectOne(new QueryWrapper<ProductDO>().eq("id", productId).eq("developer_id", developerId));
        if (productDO == null) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PRODUCT_NOT_EXIST);
        }
        BeanUtils.copyProperties(productDO, devDTO);
        List<ProductDataDO> productDataList = productDataMapper.selectList(new QueryWrapper<ProductDataDO>().eq("product_id", productId));
        devDTO.setDataList(productDataList);
        return devDTO;
    }

    private void checkProductPram(ProductDevDTO product, Date now, boolean isNew) throws ServiceException {
        // 1. 参数校验
        if (StringUtils.isEmpty(product.getTitle()) || StringUtils.isEmpty(product.getImg())) {
            throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
        }
        if (!StringUtils.isEmpty(product.getDataList())) {
            for (ProductDataDO productDataDO : product.getDataList()) {
                // 校验数据点周期
                if (productDataDO.getPeriod() <= 0 || productDataDO.getRefreshPeriod() <= 0) {
                    throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
                }
                if (productDataDO.getRefreshPeriod() * productDataDO.getPeriod() <60) {
                    throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
                }
                if (StringUtils.isEmpty(productDataDO.getIdTag()) || StringUtils.isEmpty(productDataDO.getTitle())
                        || productDataDO.getRefreshPeriod() == null || productDataDO.getPeriod() == null || productDataDO.getType() == null) {
                    throw new DeviceServiceException(DeviceExceptionDefinition.DEVICE_PARAM_CHECK_FAILED);
                }
                productDataDO.setGmtUpdate(now);
                if (isNew) {
                    productDataDO.setGmtCreate(now);
                } else {
                    if (productDataDO.getGmtCreate() == null) {
                        productDataDO.setGmtCreate(now);
                    }
                }
            }
        }
    }

}
